package com.ym.core.utils.qiniu;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.BatchStatus;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.ym.common.utils.HttpUtil;
import com.ym.core.config.ConfigCenterProperties;
import com.ym.core.config.QiNiuYunConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 七牛云SDK的封装
 *
 * @author: 李涛
 * @version: 2019年08月02日 9:43
 */
public class QiNiuUploadUtil {

	private static final Logger LOG = LoggerFactory.getLogger(QiNiuUploadUtil.class);
	/**
	 * 配置获取
	 */
	public static final QiNiuYunConfig qiniuYunConfig = SpringUtil.getBean(ConfigCenterProperties.class).getQiNiuYunConfig();

	/**
	 * 获取上传策略token
	 * 网络地址回调
	 *
	 * @param key       上传指定KEY
	 * @param qnKeyEnum 获取token类型
	 * @param callBack  是否需要回调
	 * @return
	 */
	public static String getUpToken(String key, QNKeyEnum qnKeyEnum, boolean callBack) {
		Auth auth = Auth.create(qiniuYunConfig.getAccessKey(), qiniuYunConfig.getSecretKey());
		StringMap putPolicy = new StringMap();
		if (callBack) {
			// 通过七牛第三方回调
			putPolicy.put("callbackUrl", qiniuYunConfig.getUpCallBackUrl());
			putPolicy.put("callbackBody", YamiUpPutPolicy.buildPolicy());
			putPolicy.put("callbackBodyType", MediaType.APPLICATION_JSON_VALUE);
		} else {
			// 直接调用方法上传结果
			putPolicy.put("returnBody", YamiUpPutPolicy.buildPolicy());
		}

		long expireSeconds = 3600;
		String bucket = qiniuYunConfig.getImgBucket();
		switch (qnKeyEnum) {
			case GENERAL_PICTURE_UPLOAD:
				bucket = qiniuYunConfig.getImgBucket();
				break;
			case GENERAL_REC_UPLOAD:
				bucket = qiniuYunConfig.getVoiceBucket();
				break;
			case GENERAL_DOC_UPLOAD:
				bucket = qiniuYunConfig.getDocBucket();
				break;
			case GENERAL_VIDEO_UPLOAD:
				bucket = qiniuYunConfig.getVideoBucket();
				break;
		}
		return auth.uploadToken(bucket, key, expireSeconds, putPolicy, false);
	}

	/**
	 * 获取文件前50个字节的hash值
	 *
	 * @param b
	 * @return
	 */
	private final static String getFileHexString(byte[] b) {
		StringBuilder stringBuilder = new StringBuilder();
		int byteLength = 50;
		if (b == null || b.length <= 0) {
			return null;
		} else if (b.length < byteLength) {
			byteLength = b.length;
		}
		for (int i = 0; i < byteLength; i++) {
			int v = b[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}

	/**
	 * 上传，返回上传body
	 *
	 * @param policyParams 上传自定义参数
	 * @param bytes        上传的流
	 * @param qnKeyEnum    上传的文件类型
	 * @param callBack     是否远程地址回调
	 * @return
	 */
	public static QNResponse upload(byte[] bytes, StringMap policyParams, QNKeyEnum qnKeyEnum, boolean callBack) {
		try {
			QNResponse qnResponse = new QNResponse();
			//构造一个带指定Zone对象的配置类
			Configuration cfg = new Configuration(Zone.zone1());
			//...其他参数参考类注释
			UploadManager uploadManager = new UploadManager(cfg);
			//默认不指定key的情况下，以文件内容的hash值作为文件名
			String key = QNKeyUtil.keyBuilder(qnKeyEnum, (String) policyParams.get("x:hashKey"));
			String upToken = QiNiuUploadUtil.getUpToken(key, qnKeyEnum, callBack);
			String fileExt = FileTypeUtil.getType(getFileHexString(bytes));
			if (fileExt != null) {
				LOG.info("获取到文件后缀为:{}", fileExt);
				policyParams.put("x:fileExt", fileExt);
			}
			Response response = uploadManager.put(bytes, key, upToken, policyParams, null, false);
			//解析上传成功的结果
			LOG.info(response.bodyString());
			if (response.statusCode == 200) {
				qnResponse.setId(key.substring(key.lastIndexOf("/") + 1, key.length()));
				qnResponse.setResponse(response);
				return qnResponse;
			}
		} catch (Exception ex) {
			LOG.error(ExceptionUtil.getRootCauseMessage(ex));
		}
		return null;
	}

	/**
	 * 删除文件
	 *
	 * @param keyList 要删除的KEY
	 * @return
	 */
	public static BatchStatus[] delete(List<String> keyList, String system, String bucket) {
		//重构Key
		StringBuilder prefix = new StringBuilder(system);
		switch (bucket) {
			case "images":
				prefix.append("/imgs/");
				break;
			case "documents":
				prefix.append("/docs/");
				break;
			case "videos":
				prefix.append("/videos/");
				break;
			case "voices":
				prefix.append("/recs/");
				break;
		}
		keyList = keyList.stream().map(n -> prefix.toString() + n).collect(Collectors.toList());
		//构造一个带指定Zone对象的配置类
		Configuration cfg = new Configuration(Zone.zone1());
		//...其他参数参考类注释
		String accessKey = qiniuYunConfig.getAccessKey();
		String secretKey = qiniuYunConfig.getSecretKey();
		Auth auth = Auth.create(accessKey, secretKey);
		BucketManager bucketManager = new BucketManager(auth, cfg);
		try {
			//单次批量请求的文件数量不得超过1000
			String[] keyListArray = keyList.toArray(new String[keyList.size()]);
			BucketManager.BatchOperations batch = new BucketManager.BatchOperations();
			batch.addDeleteOp(bucket, keyListArray);
			Response response = bucketManager.batch(batch);
			BatchStatus[] batchStatusList = response.jsonToObject(BatchStatus[].class);
			return batchStatusList;
		} catch (QiniuException ex) {
			LOG.error(ex.response.toString());
		}
		return null;
	}

	/**
	 * 删除文件
	 *
	 * @return
	 */
	public static Set<String> listFileKey(String system, String bucket) {
		Set<String> fileKey = new HashSet<>();
		//构造一个带指定Zone对象的配置类
		Configuration cfg = new Configuration(Zone.zone0());
		//...其他参数参考类注释
		String accessKey = qiniuYunConfig.getAccessKey();
		String secretKey = qiniuYunConfig.getSecretKey();
		Auth auth = Auth.create(accessKey, secretKey);
		BucketManager bucketManager = new BucketManager(auth, cfg);
		//文件名前缀
		StringBuilder prefix = new StringBuilder(system);
		//每次迭代的长度限制，最大1000，推荐值 1000
		int limit = 1000;
		//指定目录分隔符，列出所有公共前缀（模拟列出目录效果）。缺省值为空字符串
		String delimiter = "";

		//列举空间文件列表
		BucketManager.FileListIterator fileListIterator = bucketManager.createFileListIterator(bucket, prefix.toString(), limit, delimiter);
		while (fileListIterator.hasNext()) {
			//处理获取的file list结果
			FileInfo[] items = fileListIterator.next();
			for (FileInfo item : items) {
				int last = item.key.lastIndexOf("/") + 1;
				fileKey.add(item.key.substring(last, item.key.length()));
			}
		}
		return fileKey;
	}


	/**
	 * 删除文件
	 *
	 * @return
	 */
	public static int copy(String system, String bucket, List<String> keys, String env, boolean force) {
		//重构Key
		StringBuilder prefix = new StringBuilder(system);
		switch (bucket) {
			case "images":
				prefix.append("/imgs/");
				break;
			case "documents":
				prefix.append("/docs/");
				break;
			case "videos":
				prefix.append("/videos/");
				break;
			case "voices":
				prefix.append("/recs/");
				break;
		}
		//构造一个带指定Zone对象的配置类
		Configuration cfg = new Configuration(Zone.zone0());
		//...其他参数参考类注释
		String accessKey = qiniuYunConfig.getAccessKey();
		String secretKey = qiniuYunConfig.getSecretKey();
		Auth auth = Auth.create(accessKey, secretKey);
		BucketManager bucketManager = new BucketManager(auth, cfg);
		int success = 0;
		for (String key : keys) {
			key = prefix.toString() + key;
			try {
				String toFileKey = key.replace(system, env);
				bucketManager.copy(bucket, key, bucket, toFileKey, force);
				success++;
				LOG.info("拷贝文件成功:{}", key);
			} catch (QiniuException e) {
				LOG.error("拷贝文件失败:{}", key);
				LOG.error(ExceptionUtil.getRootCauseMessage(e));
			}
		}
		return success;
	}

	/**
	 * 检测资源是否非法
	 *
	 * @param resources 资源文件/base64文件
	 * @param base64    是否是base64
	 * @return
	 */
	public static boolean checkResourcesNormal(String resources, boolean base64) {
		String base64Prev = "data:application/octet-stream;base64,";
		String accessKey = qiniuYunConfig.getAccessKey();
		String secretKey = qiniuYunConfig.getSecretKey();
		Auth auth = Auth.create(accessKey, secretKey);
		String check = "http://ai.qiniuapi.com/v3/image/censor";
		Map<String, Object> json = new HashMap<>();
		Map<String, Object> data = new HashMap<>();
		if (base64) {
			data.put("uri", base64Prev + resources);
		} else {
			data.put("uri", resources);
		}
		json.put("data", data);
		Map<String, Object> params = new HashMap<>();
		/**
		 * pulp			识别图片中的色情、性感内容
		 * terror		识别图片中的暴恐内容。
		 * politician	识别图片中的敏感人物人脸
		 * ads			识别图片中的广告内容。
		 */
		params.put("scenes", new String[]{"pulp", "terror"});
		json.put("params", params);
		StringMap post = auth.authorizationV2(check, "POST", JSONObject.toJSONBytes(json), "application/json");
		String result = HttpUtil.post(check)
				.header("Content-Type", "application/json")
				.header("Authorization", post.get("Authorization").toString())
				.contentType("application/json")
				.send(JSONObject.toJSONString(json))
				.execute();
		JSONObject jsonResult = JSONObject.parseObject(result);
		if (jsonResult.getString("error") != null) {
			return true;
		}
		String suggestion = jsonResult.getJSONObject("result").getString("suggestion");
		/**
		 * suggestion=block：表示系统确认审核内容违规，建议您将其删除。
		 * suggestion=review：表示系统无法确认审核内容是否违规，建议您进行人工复核。
		 * suggestion=pass：表示系统确认审核内容正常，建议您忽略该文件。
		 */
		if ("block".equals(suggestion)) {
			return false;
		}
		return true;
	}
}
